ggplot系列之脑洞大开鱼缸百分比信息图~
之前看到过知乎上有人约图——
提问的帖子大概是这么写的……
https://www.zhihu.com/question/57230374
刚好趁着最近深入了解ggplot的机会,趁热打铁尝试了下,模仿的结果还算看的过眼,这里分享给大家具体的代码步骤。
(首先声明一下,个人并不主张使用这种奇巧淫技,其实图表够用就好,表达的意思到位,看着不辣眼睛基本也就合格了,图表是为了高效传递视觉信息,提升信息传递效率的。各种花式炫技巧其实都会来带不同程度的视觉呈现弊端,会让读图者分心,反而不能聚焦于图表本身所传达的信息,这里仅仅是为了扩展练习ggplot的技能而已,并非有意引导大家去追求这种图表)。
最后做出来的效果大致是这样的:
以下是整个图表数据源的构思过程和图表制作过程:
library(tidyverse)
require(grid)
library(Rmisc)
该图表的大体思路是通过多边形填充(也就是之前多次做过的数据地图的映射方式),为啥不用面积图呢,因为有圆弧,面积图是堆积的无法用来刻画圆弧,但是只要坐标点取的足够多,我们就可以通过坐标点组成的多边形(这里是圆形)来进行填充构造圆形。
因为有五个等圆,所以数据分成五组,但是因为大圆要描边,而百分比以下的圆缺又不规则,上面都有不同程度被切掉的部分,所以整体的制作思路分成两大步:
先分别构造五个大圆坐标点,用于图形描边:
(只需构造第一个,然后平移水平坐标即可)
将以上五个大圆的数据copy一份,分别按照实际指标,去掉上方圆缺的部分,只保留下方对应的百分比部分(比例可以通过直径进行换算)。
最后图层映射的时候一定要先映射被切割后的残缺园数据(使用geom_ploygon图层函数),然后再使用完整大圆的数据以geom_path()或者geom_line()图层的形式进行映射。(还不知道path和line的区别的小伙伴儿,赶紧去查ggplot的官方文档)。
构造第一个园在笛卡尔坐标系中的位置:
px1<-seq(from=0,to=10,length=1000)
py1<-sqrt(5^2-(px1-5)^2)
Project1x<-c(px1,rev(px1))
Project1y<-c(py1,-py1)
其实还是求解圆形的曲线方程啦,就跟之前做的那个太极图一模一样的~
----------------------------------------------------------------------------------------------------------
做好第一个大圆的坐标点后,剩下就是换参数,换名称而已啦~
Project1<-data.frame(lon=Project1x,lat=Project1y)
Project1$group<-"ProjectA"
Project1$order<-1:nrow(Project1)
Project2<-data.frame(lon=Project1x+15,lat=Project1y)
Project2$group<-"ProjectB"
Project2$order<-1:nrow(Project2)
Project3<-data.frame(lon=Project1x+30,lat=Project1y)
Project3$group<-"ProjectC"
Project3$order<-1:nrow(Project3)
Project4<-data.frame(lon=Project1x+45,lat=Project1y)
Project4$group<-"ProjectD"
Project4$order<-1:nrow(Project4)
Project5<-data.frame(lon=Project1x+60,lat=Project1y)
Project5$group<-"ProjectE"
Project5$order<-1:nrow(Project5)
Project<-rbind(Project1,Project2,Project3,Project4,Project5)
这个数据集是五个大圆的坐标点数据:
ggplot(Project)+geom_path(aes(lon,lat,group=group))
如果出来是这样差不多就表明你描边的数据做成功了!
--------------------------------------------------------------------------------------------------------------------------
接下来就是构造哪些实际百分比的大圆部分,即根据具体的百分比指标,切掉上方的哪些多余部分:
具体怎么切呢,很简单的,就是使用切片函数,把只保留哪些对应百分比以下的坐标点(所以要统一根据y值进行筛选)。
这里注意了,前三个图表因为筛完之后,整体的数据点连线后是有缺口的,需要在现有数据最后增加一个观测值,该观测值横纵坐标与第一个观测值相同(注意是切掉多余的部分之后的第一个观测值,也就是月牙形状的右边角点所在的坐标,这里最初的整圆坐标点是从180~-180的顺序构造的),而后两个图表即便切掉上半边之后,连线仍然是可以首尾衔接的,因为这两个被切之后首尾点几乎仍然是相邻的(无限接近,因为整圆是由2000个坐标点构成的)。
以下是百分比部分的多边形数据构造过程:(注意按照上面提示的文字部分体会以下前三个图和后两个图过程有啥区别)
Proj1<-Project1[,1:2]%>%filter(lat<=-4)
Proj1[nrow(Proj1)+1,]<-c(8,-4)
Proj1$group<-"ProjA"
Proj1$order<-1:nrow(Proj1)
Proj2<-Project2[,1:2]%>%filter(lat<=-3)
Proj2[nrow(Proj2)+1,]<-c(24,-3)
Proj2$group<-"ProjB"
Proj2$order<-1:nrow(Proj2)
Proj3<-Project3[,1:2]%>%filter(lat<=0)
Proj3[nrow(Proj3)+1,]<-c(40,0)
Proj3$group<-"ProjC"
Proj3$order<-1:nrow(Proj3)
Proj4<-Project4[,1:2]%>%filter(lat<=3)
Proj4$group<-"ProjD"
Proj4$order<-1:nrow(Proj4)
Proj5<-Project5[,1:2]%>%filter(lat<=4)
Proj5$group<-"ProjE"
Proj5$order<-1:nrow(Proj5)
Projdata<-rbind(Proj1,Proj2,Proj3,Proj4,Proj5)
最后组合成一个数据框,如果这次出图如下效果,说明你多边形的数据也构造成功了!
标签数据:
labeldata<-data.frame(x=seq(from=5,to=65,length=5),y=c(-4,-3,0,3,4),label=sprintf("%2d%%",c(10,20,50,80,90)))
以下就是该案例的最终成品图:
p1<-ggplot()+
geom_polygon(data=Projdata,aes(x=lon,y=lat,group=group),fill="#92D24F",col=NA)+
geom_path(data=Project,aes(x=lon,y=lat,group=group),col="black",size=1.2)+
geom_text(data=labeldata,aes(x=x,y=y+1,label=label),hjust=.5)+
scale_x_continuous(breaks=labeldata$x,labels=paste0("Project",LETTERS[1:5]))+
ylim(-5.5,6)+
theme_minimal()+
theme(
panel.grid=element_blank(),
axis.title=element_blank(),
axis.text.y=element_blank(),
plot.margin = unit(c(.2,.2,1,.2), "cm")
)
p2<-ggplot()+
geom_polygon(data=Projdata,aes(x=lon,y=lat,group=group),fill="#FFC000",col=NA)+
geom_path(data=Project,aes(x=lon,y=lat,group=group),col="black",size=1.2)+
geom_text(data=labeldata,aes(x=x,y=y+1,label=label),hjust=.5)+
scale_x_continuous(breaks=labeldata$x,labels=paste0("Project",LETTERS[1:5]))+
ylim(-5.5,6)+
theme_minimal()+
theme(
panel.grid=element_blank(),
axis.title=element_blank(),
axis.text.y=element_blank(),
plot.margin = unit(c(.2,.2,1,.2), "cm")
)
为了方便作图,这里给大家介绍ggplot系统中如何进行多图排版,但本案例排版比较简单,详细的参数可以自己去查。
方法一:
grid.newpage()
pushViewport(viewport(layout=grid.layout(2,2)))
vplayout <- function(x,y){viewport(layout.pos.row = x, layout.pos.col = y)}
print(p1,vp=vplayout(1,1:2))
print(p2,vp=vplayout(2,1:2))
方法2:
library(gridExtra)
library("plyr")
library("lattice")
multiplot(p1,p2,layout=matrix(c(1,1,2,2),nrow=2,byrow=TRUE))
本案例旨在让大家体会多边形这种图层在ggplot诸多图层函数中的应用,该图层函数相当强大,它可以做很多创意图表的填充工作,而之前使用的geom_rect()图层相比就要逊色很多,他只能映射出那种很规整的矩形块,而如若遇到该类型案例这种有不规则曲边或者圆弧,那么就傻眼了,所以关键时刻,还是得学会灵活运用。
至于geom_ploygon这个图层到底有多强大,之前推送过的所有涉及数据地图的部分,全部都要归功于它,详细的介绍,我会单列一一节讲解,大家稍安勿躁。
作者简介:
欢迎关注魔方学院QQ群